You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix: make StepRegistry atomic and raise on corruption - #3922
StepRegistry._load() silently returned empty defaults on corruption, causing installed steps to appear to vanish. save() wrote directly to the target file risking data loss.
Fix
_load() now raises OSError on symlinked paths and corrupted files. save() now uses atomic writes via mkstemp + os.replace.
…nt data loss
1. _load() now raises OSError on symlinked paths and corrupted files
instead of silently returning empty defaults (matches WorkflowRegistry)
2. save() now writes to a temp file and atomically replaces, preventing
data loss from truncated writes on crash
…mic-save
Resolve conflicts by taking main's split of workflows/catalog.py: the
StepRegistry code in this branch belongs in workflows/step/catalog/_domain.py,
so main's workflows/catalog/_domain.py is taken as-is instead of carrying a
duplicate StepRegistry, and tests/test_workflows.py takes main's version with
the branch's new tests re-added on top.
Assisted-by: opencode (model: mimo-v2.6-flash-free, autonomous)
Port the branch's StepRegistry hardening to where main now keeps the class
(workflows/step/catalog/_domain.py): _load raises OSError for a symlinked
path, an unreadable file, unparseable JSON, or a non-registry shape instead
of silently returning an empty registry, and save writes through a
temp file plus os.replace so a crash cannot leave a half-written registry.
The new constructor errors would otherwise surface as tracebacks, and the
step commands used to reach their directory-symlink check only because the
registry swallowed the fault. A shared _load_step_registry_or_exit helper now
prints a clean error, and add/remove resolve the steps base directory first so
the symlinked-directory message keeps its specific wording.
Five tests that pinned the silent-reset behaviour are updated to the fail-loud
contract, and the branch's three regression tests (symlink, corrupted JSON,
atomic save) come along.
Assisted-by: opencode (model: mimo-v2.6-flash-free, autonomous)
…mic-save
Take main's refactor in the conflicted step files: the registry hardening in
this branch lives in workflows/step/catalog/_domain.py, while main has moved
the CLI-coupled logic into workflows/step/installer.py (resolve_steps_base_dir,
StepInstallError, the install transaction) and rewritten workflow_step_remove
around _remove_step_locked. tests/test_workflows.py merges with the branch's
registry tests kept on top of main's version.
Assisted-by: opencode (model: mimo-v2.6-flash-free, autonomous)
StepRegistry._load returned an empty registry for a symlinked path, an
unreadable file, unparseable JSON, or a non-registry shape, so corruption
reached callers as a silently reset registry. It now raises OSError with the
registry path in the message; save() already writes atomically on main.
Because the constructor now raises, translate the error where it surfaces:
installer._load_step_registry maps OSError onto StepInstallError so the
existing command handlers keep printing a clean error, and the new
_helpers._load_step_registry_or_exit does the same for the step commands.
workflow_step_remove resolves the steps base directory before loading the
registry so the symlinked-directory message keeps its specific wording.
Tests: the three fail-loud regression tests (symlinked file, corrupted JSON,
atomic save) are kept, and five tests that pinned the silent-reset behaviour
now assert the raised OSError instead.
Assisted-by: opencode (model: mimo-v2.6-flash-free, autonomous)
Replaces the direct _load_step_registry() call with a full
install_step_package() invocation using a valid package and a
corrupted registry, asserting no destination or staging directory
is created.
Assisted-by: GitHub Copilot (autonomous)
Assert save() raises the specific StepValidationError type
tests/test_workflows.py:11489
This assertion accepts every Exception, so it cannot verify save()'s documented error contract and would still pass if an unrelated exception escaped. Since the write path wraps I/O failures as StepValidationError, assert that specific type.
The glob tmp* does not match _WORK_DIR_PREFIX (.speckit-step-install-),
so a leaked staging directory would go unnoticed.
Assisted-by: GitHub Copilot (autonomous)
This still does not make corruption fail loudly for every reader: bundles/references.py:18-60 catches this OSError as a best-effort lookup failure and returns False. Consequently, bundle validate can still treat an installed step as absent (or merely emit an offline/catalog warning), reproducing the “steps appear to vanish” behavior described by this PR. Propagate or explicitly translate registry corruption in that caller instead of collapsing it into “not installed,” with a bundle-reference regression test.
Add a negative regression test for the remove command with a corrupted registry, asserting a clean exit and that the existing step directory remains untouched. This newly routed path precedes destructive removal, but only step list and installation are covered; the repository requires relevant prevented-behavior tests, and tests/specify_cli/workflows/step/test_command_remove.py is the mirrored public-command suite.
Assert save raises StepValidationError
tests/test_workflows.py:11489
Exception subsumes OSError, so this assertion accepts any ordinary exception and does not verify save()'s public failure contract. Since save() wraps write failures as StepValidationError, assert that exact type so a regression that leaks an unrelated exception cannot pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
StepRegistry._load()silently returned empty defaults on corruption, causing installed steps to appear to vanish.save()wrote directly to the target file risking data loss.Fix
_load()now raisesOSErroron symlinked paths and corrupted files.save()now uses atomic writes via mkstemp + os.replace.